package lumis.service.seo.sitemap;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import lumis.portal.PortalException;
import lumis.portal.PortalObjectNotFoundException;
import lumis.portal.authentication.SessionConfig;
import lumis.portal.channel.ChannelConfig;
import lumis.portal.manager.ManagerFactory;
import lumis.portal.page.IPageManager;
import lumis.portal.page.PageConfig;
import lumis.portal.servicecontainer.ServiceContainerUrl;
import lumis.util.ITransaction;


/**
 * Generates the sitemap xml using only visible channels and pages in the hierarchy of a channel.
 * 
 * @version $Revision: 10825 $ $Date: 2009-08-04 12:36:40 -0300 (Tue, 04 Aug 2009) $
 * @since 5.0.0
 */
class SitemapGenerator
{
	/**
	 * Creates a sitemap xml, using the given channel identifiers.
	 * 
	 * @param sessionConfig the user session information.
	 * @param channelIds the channel identifiers.
	 * @param transaction the transaction for persistence access.
	 * @return the sitemap.xml.
	 * @since 5.0.0
	 */
	protected static String createSitemap(SessionConfig sessionConfig, Collection<String> channelIds, ITransaction transaction) throws PortalException
	{
		StringBuilder sb = new StringBuilder();
		
		//print the xml header
		sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?> \n");
		
		sb.append("<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"> \n");
		for(String channelId: channelIds)
		{
			List<Object> structureElements = getVisibleChannelTree(sessionConfig, channelId, transaction);
			List<String> elementsPath = getElementsPath(sessionConfig, structureElements, transaction);
			appendUrlElements(sessionConfig, sb, elementsPath, transaction);
		}
		sb.append("</urlset>");
		
		return sb.toString();
	}
	/**
	 * Creates a sitemap xml, using the given channel identifiers.
	 * 
	 * @param sessionConfig the user session information.
	 * @param channelIds the channel identifiers.
	 * @param baseUrl the absolute url for the portal application root.
	 * @param transaction the transaction for persistence access.
	 * @return the sitemap xml.
	 * @since 5.0.0
	 */
	protected static String createSitemap(SessionConfig sessionConfig, Collection<String> channelIds, String baseUrl, ITransaction transaction) throws PortalException
	{
		StringBuilder sb = new StringBuilder(100 + 100 * channelIds.size());
		
		//print the xml header
		sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?> \n");
		
		sb.append("<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"> \n");
		for(String channelId: channelIds)
		{
			List<Object> structureElements = getVisibleChannelTree(sessionConfig, channelId, transaction);
			List<String> elementsUrls = getElementsUrl(structureElements, baseUrl);
			appendUrlElements(sessionConfig, sb, elementsUrls, transaction);
		}
		sb.append("</urlset>");
		
		return sb.toString();
	}

	/**
	 * Generates <code>url</code> elements for the sitemap xml. 
	 * 
	 * @param sessionConfig the user session information.
	 * @param urls the list with the urls.
	 * @param transaction the transaction for persistence access.
	 * @since 5.0.0
	 */
	private static void appendUrlElements(final SessionConfig sessionConfig, 
			StringBuilder stringBuilder, List<String> urls, ITransaction transaction) throws PortalException
	{
		for(String url: urls)
		{
			stringBuilder.append("   <url>\n");
			stringBuilder.append("      <loc>" + url + "</loc>\n");
			stringBuilder.append("   </url>\n");
		}
	}

	/**
	 * This method returns the pages and channels visible inside a 
	 * channel hierarchy.
	 * 
	 * @param sessionConfig the user session information.
	 * @param rootChannelId the id of the root channel.
	 * @param transaction the transaction for persistence access.
	 * @return a list containing the corresponding {@link ChannelConfig} and {@link PageConfig}.
	 * @since 5.0.0
	 */
	private static List<Object> getVisibleChannelTree(SessionConfig sessionConfig, String rootChannelId, ITransaction transaction) throws PortalException
	{
		ChannelConfig channelConfig;
		try
		{
			channelConfig = ManagerFactory.getChannelManager().get(sessionConfig, rootChannelId, transaction);
		}
		catch (PortalObjectNotFoundException e)
		{
			return Collections.emptyList();
		}
		List<Object> pagesAndChannels = new ArrayList<Object>();
		//check if the channel isn't hidden
		if(!channelConfig.isHidden())
		{
			// add the root channel
			pagesAndChannels.add(channelConfig);
			// add pages 
			IPageManager pageManager = ManagerFactory.getPageManager();
			List<String> pageIds = pageManager.getIdsByParentChannelId(sessionConfig, rootChannelId, transaction);
			for (String pageId: pageIds)
			{
				PageConfig pageConfig = ManagerFactory.getPageManager().get(sessionConfig, pageId, transaction);
				//check if the page isn't hidden
				if(!pageConfig.isHidden())
					pagesAndChannels.add(pageConfig);	
			}
			
			List<String> channelIds = ManagerFactory.getChannelManager().getIdsByParentChannelId(sessionConfig, rootChannelId, transaction);
			for(String channelId: channelIds)
				pagesAndChannels.addAll(getVisibleChannelTree(sessionConfig, channelId, transaction));
		}
		
		return pagesAndChannels;
	}

	/**
	 * Returns a list containing the paths of the given pages and channels.
	 * 
	 * @param sessionConfig the user session information.
	 * @param structureElements the list of {@link ChannelConfig} and/or {@link PageConfig} objects.
	 * @param transaction the transaction for persistence access.
	 * @return a list with the paths of the given structure elements, in the same order.
	 * @since 5.0.0
	 */
	protected static List<String> getElementsPath(SessionConfig sessionConfig, List<Object> structureElements, ITransaction transaction) throws PortalException
	{
		List<String> pathList = new ArrayList<String>(structureElements.size());
		
		if(structureElements !=  null)
		{
			for(Object structureElement: structureElements)
			{
				if (structureElement instanceof PageConfig)
				{
					String pageId = ((PageConfig) structureElement).getId();
					pathList.add(ManagerFactory.getPageManager().getPath(sessionConfig, pageId, transaction));
				}					
				else
				{
					String channelId = ((ChannelConfig)structureElement).getId();
					pathList.add(ManagerFactory.getChannelManager().getPath(sessionConfig, channelId, transaction));
				}
			}
		}

		return pathList;
	}
	
	/**
	 * Returns a list containing the URLs for the given pages and channels.
	 * 
	 * @param sessionConfig the user session information.
	 * @param structureElements the list of {@link ChannelConfig} and/or {@link PageConfig} objects.
	 * @param transaction the transaction for persistence access.
	 * @return a list with the URLs of the given structure elements, in the same order.
	 * @since 5.0.0
	 */
	protected static List<String> getElementsUrl(List<Object> structureElements, String baseUrl)
	{
		List<String> urlList = new ArrayList<String>(structureElements.size());
		if(structureElements != null)
		{
			for(Object structureElement: structureElements)
			{
				ServiceContainerUrl containerUrl = new ServiceContainerUrl(ServiceContainerUrl.RENDER_URL);
				if (structureElement instanceof PageConfig)
					containerUrl.setPageId(((PageConfig)structureElement).getId());
				else
					containerUrl.setChannelId(((ChannelConfig)structureElement).getId());
				
				urlList.add(baseUrl + containerUrl.toString());
			}
		}
		return urlList;
	}
}